// -----------------------------------------------------------------------
//  <copyright file="EntityResourceExtractor.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2025 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2025/1/5 12:10</last-date>
// -----------------------------------------------------------------------

using System.ComponentModel.DataAnnotations.Schema;
using System.Reflection;

using DaprPlus.Builder;

using OSharp.Reflection;


namespace DaprPlus.Authorization.Application;

public class EntityResourceExtractor(IServiceProvider provider) : ResourceExtractorBase(provider)
{
    private readonly IServiceProvider _provider = provider;
    private Dictionary<string, ResourceOperation[]> _entityOperationsMap = [];

    /// <summary>
    /// 获取 资源类型
    /// </summary>
    public override ResourceType ResourceType => ResourceType.Entity;

    protected override Task<IResource[]> ExtractCoreAsync(string serviceName)
    {
        var list = new List<IResource>();
        var context = _provider.GetRequiredService<DaprPlusContext>();
        _entityOperationsMap = GetAuthConfigsDict(context);
        var entityTypes = context.FindTypesByBase<IEntity>()
            .Where(m => !m.IsAbstract && !m.HasAttribute<NotMappedAttribute>()).ToArray();
        if (entityTypes.Length == 0)
        {
            return Task.FromResult(list.ToArray());
        }

        foreach (var entityType in entityTypes)
        {
            var resource = ExtractResource(entityType, serviceName);
            if (resource == null)
            {
                continue;
            }
            list.Add(resource);
        }

        return Task.FromResult(list.ToArray());
    }
    
    /// <summary>
    /// 重写以实现从类型中提取资源信息
    /// </summary>
    /// <param name="type">类型</param>
    /// <param name="serviceName">服务名称</param>
    /// <returns></returns>
    protected virtual IResource? ExtractResource(Type type, string serviceName)
    {
        if (!type.IsBaseOn<IEntity>())
        {
            return null;
        }

        var resource = new Resource
        {
            Name = type.GetDescription(),
            Code = type.FullName ?? type.Name,
            ServiceName = serviceName,
            Type = ResourceType.Entity,
            AccessType = ResourceAccessType.None,
            IsSystem = true
        };

        // 从DaprPlusContext的ApiRelateEntityAllowedOperations中提取相应实体类型的操作集合
        if (_entityOperationsMap.TryGetValue(type.FullName!, out var operations) && operations.Length > 0)
        {
            // 设置允许的操作列表
            resource.AllowedOperationsJson = operations.ToTextJson();
        }

        var metadata = GetMetadata(type);
        resource.MetadataJson = metadata.ToTextJson();

        return resource;
    }

    private Dictionary<string, ResourceOperation[]> GetAuthConfigsDict(DaprPlusContext context)
    {
        var dict = context.FindTypesByAttribute<AuthConfigAttribute>()
            .Select(type => type.GetAttribute<AuthConfigAttribute>())
            .GroupBy(att => att.ResourceType)
            .ToDictionary(
                g => g.Key.FullName!,
                g => g.Select(m => new { m.Operation, m.AccessType})
                    .Select(n => new ResourceOperation() { Code = n.Operation, Display = n.Operation, AccessType = n.AccessType})
                    .ToArray());
        return dict;
    }

    private static EntityMetadata GetMetadata(Type type)
    {
        var metadata = new EntityMetadata()
        {
            TypeName = type.FullName!,
            AuditEnabled = true
        };

        var propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
        var exceptNames = new[] { "DeletedTime", "IsDeleted", "IntegrationEvents", "DomainEvents" };
        var props = propertyInfos.Where(m => !exceptNames.Contains(m.Name) && !m.IsVirtual()).Select(property =>
        {
            var ep = new EntityProperty
            {
                Name = property.Name,
                Display = property.GetDescription(),
                TypeName = property.PropertyType.FullName ?? property.PropertyType.Name
            };
            //枚举类型，获取枚举项作为取值范围
            if (property.PropertyType.IsEnum)
            {
                ep.TypeName = typeof(int).FullName ?? "System.Int32";
                var enumType = property.PropertyType;
                var values = enumType.GetEnumValues();
                var intValues = values.Cast<int>().ToArray();
                var names = values.Cast<Enum>().Select(m => m.ToDescription()).ToArray();
                for (var i = 0; i < intValues.Length; i++)
                {
                    var value = intValues[i].ToString();
                    ep.ValueRange.Add(new { id = value, text = names[i] });
                }
            }

            return ep;
        }).ToArray();
        props = props.OrderByDescending(m => m.Name == "Id").ToArray();
        metadata.Properties = props;
        return metadata;
    }
}
